package io.yx.desktop.web;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.FileUpload;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.*;
import lombok.extern.log4j.Log4j2;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @auth YX
 * @date 2022/7/14 20:42
 */
@Log4j2
public class VertxServer {

    // 消息列表
    private static final List<String> messageList = new ArrayList<>();

    private Vertx vertx = Vertx.vertx();
    HttpServer httpServer = vertx.createHttpServer();
    Router router = Router.router(vertx);

    public Future<HttpServer> start(int port, String rootPath) {
        router.route().handler(rc -> {
            rc.addHeadersEndHandler(handler -> {
                // 解决staticHandler重定向后未重新编码URL导致访问路径变成问号???的问题
                int statusCode = rc.response().getStatusCode();
                if (HttpResponseStatus.MOVED_PERMANENTLY.code() == statusCode) {
                    String location = rc.response().headers().get(HttpHeaders.LOCATION);
                    if (StrUtil.isNotBlank(location)) {
                        log.info("301重新编码url:{},encodeURL:{}", location, URLUtil.encode(location));
                        rc.response().putHeader(HttpHeaders.LOCATION, URLUtil.encode(location));
                    }
                }
            });
            rc.next();
        });
        router.route().handler(LoggerHandler.create());

        // 默认路由
        router.route().handler(StaticHandler.create("static")
                .setDefaultContentEncoding(StandardCharsets.UTF_8.name())
                .setCachingEnabled(false).setIndexPage("index.html"));

        // 文件请求路由
        router.route("/file/*").handler(StaticHandler.create(FileSystemAccess.ROOT, rootPath)
                .setDefaultContentEncoding(StandardCharsets.UTF_8.name())
                .setDirectoryListing(true)
                .setCachingEnabled(false)
        );

        // 如果请求出现404错误,尝试将其路由到/file目录,因为可能是由于静态资源服务器中的html发送的js/css等相对路径请求
        router.errorHandler(404, rc -> {
            // 将所有非/file/开头的请求都加上/file/,类似于nginx的rewrite
            String normalizedPath = rc.normalizedPath();
            if (!StrUtil.startWith(normalizedPath, "/file/")) {
                String reroutePath = "/file" + normalizedPath;
                log.debug("原地址:{}; 重定向地址:{}", normalizedPath, reroutePath);
                rc.redirect(reroutePath);
            } else {
                rc.response().setStatusCode(404);
                rc.end();
            }
        });

        // 自定义获取文件列表
        router.route("/fileList").handler(rc -> {
            rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
            // 获取共享文件目录中的文件
            List<String> fileList = FileUtil.listFileNames(rootPath);
            List<String> files = fileList.stream()
                    .filter(i -> FileUtil.isFile(FileUtil.file(rootPath, i)))
                    .collect(Collectors.toList());
            String jsonStr = JSONUtil.toJsonStr(files);
            rc.end(jsonStr);
        });
        // 自定义获取消息列表
        router.route("/messageList").handler(rc -> {
            rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
            String jsonStr = JSONUtil.toJsonStr(messageList);
            rc.end(jsonStr);
        });

        // 添加消息
        router.route("/addMessage").handler(rc -> {
            rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
            String message = rc.request().getParam("message");
            messageList.add(message);
            rc.end(new JSONObject().set("code", "0").toString());
        });

        router.route("/uploadFile").handler(BodyHandler.create(rootPath)).handler(rc -> {
            rc.response().putHeader(HttpHeaders.CONTENT_TYPE, "application/json;charset=utf-8");
            List<FileUpload> fileUploads = rc.fileUploads();
            for (FileUpload fileUpload : fileUploads) {
                String fileName = fileUpload.fileName();
                String tempFileName = fileUpload.uploadedFileName();
                log.info("文件名：{}，临时文件名：{}", fileName, tempFileName);
                FileUtil.rename(FileUtil.file(tempFileName), fileName, true);
            }
            rc.end(new JSONObject().set("code", "0").toString());
        });

        router.route().handler(FaviconHandler.create(vertx, "static/favicon.ico"));
        httpServer.requestHandler(router);
        return httpServer.listen(port);
    }

    public void stop() {
        httpServer.close();
    }

}
